#pragma once

#include "base_compat.h"
#include "result.h"
#include <boost\detail\interlocked.hpp>

#include <mutex>
#include <list>
#include <atomic>

namespace space {
	/*
	** This is the base class to allow a global object reference counter as well
	**/
	class global_reference_counter {
	public:
		static u_int __inline global_reference_count( void ) { 
			return _all_reference_count;
		}

		static u_int __inline global_instance_count( void ) { 
			return _all_instance_count;
		}

	protected:
		static volatile u_int _all_instance_count;
		static volatile u_int _all_reference_count;

	};


	/*
	** Per-Type reference counting template class
	**/
	template <class T> 
	class reference_counter : public global_reference_counter {

	public:
		reference_counter( void ) : _uLocalReferenceCount(0) {
			_instance_count++;
			_all_instance_count++;
		}

		~reference_counter( void ) {
			_instance_count--;
			_all_instance_count--;
		}

		result __inline attach( void ) { 
			_reference_count++;
			_uLocalReferenceCount++;
			_all_reference_count++;			// global counters must always be Interlocked

			return RESULT_OK;
		}

		result __inline detach( void ) {
			if(_reference_count > 0) { 
				_reference_count--;
			}
			if(_all_reference_count > 0) {
				_all_reference_count--;		// global counters must always be Interlocked
			}
			if(_uLocalReferenceCount > 0) {
				_uLocalReferenceCount--;
			}

			return RESULT_OK;
		}

		static u_int reference_count(  ) { 
			return _reference_count;
		}

		static u_int instance_count( ) { 
			return _instance_count;
		}

	protected:
		static volatile u_int _instance_count;
		static volatile u_int _reference_count;
		volatile u_int _uLocalReferenceCount;
	};


	/*
	** Interlocked Per-Type reference counting template class
	**/
	template <class T> 
	class atomic_reference_counter : public global_reference_counter {

	public:
		atomic_reference_counter( void ) : _uLocalReferenceCount(0) {

			_instance_count++;
			_all_instance_count++;
		}

		~atomic_reference_counter( void ) {
			_instance_count--;
			_all_instance_count--;
		}

		result __inline attach( void ) { 
			_reference_count++;
			_uLocalReferenceCount++;
			_all_reference_count++;

			return RESULT_OK;
		}

		result __inline detach( void ) {
			int uiRefCount, uiAllRefCount;

			uiRefCount = _reference_count;
			uiAllRefCount = _all_reference_count;

			if(uiRefCount > 0) { 
				_reference_count--;
			}
			if(uiAllRefCount > 0) {
				_all_reference_count--;
			}
			if(_uLocalReferenceCount > 0) {
				_uLocalReferenceCount--;
			}

			return RESULT_OK;
		}

		static u_int reference_count( ) { 
			return _reference_count;
		}

		static u_int instance_count(  ) { 
			return _instance_count;
		}

	protected:
		static std::atomic<u_int> _instance_count;
		static std::atomic<u_int> _reference_count;
		std::atomic<u_int> _uLocalReferenceCount;

	};


#ifdef _SPACE_DEBUG_REFERENCE_TRACK
	template<class T>
	class reference_tracker : 
		public atomic_reference_counter<T> {
	public:
		reference_tracker() : atomic_reference_counter<T>() {
			_instances_mutex.lock();
			_instances.push_back((T *)this);
			_instances_mutex.unlock();
		}
		~reference_tracker() {
			// find us, and remove us from the list
			_instances_mutex.lock();

			std::list<T *>::iterator iter;
			for(iter = _instances.begin(); 
				iter != _instances.end();
				iter++) {
					
					if(*iter == (T *)this) 
						break;
			}
			if(iter != _instances.end())
				_instances.erase(iter);

			_instances_mutex.unlock();
		}

		static u_int instances(std::vector<T *> & instances) {
			_instances_mutex.lock();

			std::list<T *>::iterator iter;
			for(iter = _instances.begin(); 
				iter != _instances.end();
				iter++) {
					instances.push_back(*iter);
			}

			_instances_mutex.unlock();

			return instances.size();
		}
	protected:
		static std::mutex _instances_mutex;
		static std::list<T *> _instances; 
	};
#else
	template<class T>
	class reference_tracker : 
		public reference_counter<T> {
			static u_int instances(std::vector<T *> & instances) {
				return 0;
			}
	};
#endif

	template <typename T> volatile unsigned int reference_counter<T>::_instance_count = 0;
	template <typename T> volatile unsigned int reference_counter<T>::_reference_count = 0;
	template <typename T> std::atomic<u_int> atomic_reference_counter<T>::_instance_count = 0;
	template <typename T> std::atomic<u_int> atomic_reference_counter<T>::_reference_count = 0;
	template <typename T> std::list<T *> reference_tracker<T>::_instances;
	template <typename T> std::mutex reference_tracker<T>::_instances_mutex;
};